#include <stdarg.h>
#include <stdlib.h>
#include <stdio.h>
#include <assert.h>
#include <string.h>
#include "array2d.h"
#include "object.h"
#include "new.h"

static Pointer array2d_create(Pointer _self, va_list *app)
{
	struct Array2d* a = _self;
	int i;
	a->width = va_arg(*app, size_t);
	a->height = va_arg(*app, size_t);
	a->data = (struct Object*)calloc(a->width*a->height, sizeof(struct Object));
	for(i = 0; i < a->width*a->height; i ++)
	{
		placement_new(Object, (Pointer*)((char*)a->data + sizeof(struct Object)*i), 0, 0, 0);
	}
	return a;
}

static Pointer array2d_destroy(Pointer _a)
{
	int i;
	struct Array2d* a = _a;
	assert(a);

	for(i = 0; i < a->width * a->height; i++)
	{
		placement_delete( (char*)a->data + i*sizeof(struct Object)) ;
	}

	free(a->data);
	return a;
}


Pointer array2d_cell_access(Pointer _a, const size_t x, const size_t y)
{
	struct Array2d* a = _a;
	if(a)
	{
		if( (a->width<=x) || (a->height<=y) || (x<0) || (y<0))
		{
			return NULL;
		}
		else
		{
			return (Pointer)((char*)a->data + sizeof(struct Object)*(y*a->width + x));
		}
	}
	return NULL;
}


int array2d_copy(Pointer _Dst, Pointer _Src, const size_t x, const size_t y)
{
	struct Array2d* Dst = _Dst;
	struct Array2d* Src = _Src;
	size_t calc_width, calc_height, i;
	if(!Src || !Dst)
	{
		return 0;
	}
	if((x>=Dst->width) || (y>=Dst->height) || (x<0) || (y<0))
	{
		return 0;
	}
	calc_height = Dst->height - y;
	calc_width = Dst->width - x;
	if(Src->width < calc_width)
	{
		calc_width = Src->width;
	}
	if(Src->height < calc_height)
	{
		calc_height = Src->height;
	}
	for(i=0; i<calc_height; i++)
	{
		memcpy((Pointer)((char*)Dst->data + sizeof(struct Object)*((y+i)*Dst->width + x)), (Pointer)((char*)Src->data + sizeof(struct Object)*(Src->width*i)), calc_width*sizeof(struct Object));
	}
	return 1;
}

struct Array2d* array2d_resize(Pointer* _arr, const size_t x, const size_t y)
{
	if(_arr && (x>=0) && (y>=0))
	{
		struct Array2d* new_arr;
		struct Array2d** arr = _arr;
		new_arr = new(Array2d, x, y);
		array2d_copy(new_arr, *arr, 0, 0);
		delete(*arr);
		*arr = new_arr;
		return *arr;
	}
	return NULL;
}

static const struct Class _Array2d = {
    sizeof(struct Array2d),			// size
    array2d_create,                 // ctor
    array2d_destroy                 // dtor
};
 
const void *Array2d = &_Array2d;